/***********************************************************************************************************************
 based on:
         @author 	Andreas Weber, webweber@motiondraw.com
   	 @version 	  1.1   (April 18, 2008) port to AS3 
				[ 1.0   (March 05, 2005) AS2 Version] 
				
   
	 This class provides 2 line gereralization functions:
	 Lang Simplification and McMaster's Slide Averaging Algorithm
	 
	 The purpose and principle of these algorithms are perfectly explained in this tutorial: 
	 http://www.sli.unimelb.edu.au/gisweb/LGmodule/LGSelect.htm (thanks to Jim Cheng for pointing this out)


        Ported to Processing by Florian Weil (www.derhess.de)
***********************************************************************************************************************/

class LineGeneralization {
		
  LineGeneralization() {
    //println("LineGeneralization constructor");
  }
  
  ArrayList<PVector> smoothMcMaster(ArrayList<PVector> srcList) {
      
      int len = srcList.size();
      ArrayList<PVector> nL = new ArrayList<PVector>(len);
      println("smooth" + len);
      if(len < 5) { 
        return srcList;
      }
      PVector tmp;    	
      int j;
      int avX;
      int avY;
      int i = len -1;
      while(i > 0) {
        println("machwas " + i);
        if(i==len-1 || i==len-2 || i==1 || i==0) {
          tmp = srcList.get(i);
          nL.add(new PVector(tmp.x, tmp.y));
        } else {
          j=5;
          avX = 0; 
          avY = 0;
          int ti;
          while(j > 0) {
            ti = i+2-j;
            if(ti >= 0) {
              tmp = srcList.get(i+2-j);
              avX += tmp.x;
              avY += tmp.y;
            }
            j--;
          }
          avX = avX/5; 
          avY = avY/5;
          tmp = srcList.get(i);
          nL.add(new PVector((tmp.x+avX)/2, (tmp.y+avY)/2));
        }
        i--;
      }
      return nL;
  }
		
		

  ArrayList<PVector> simplifyLang(float lookAhead, float tolerance, ArrayList<PVector> srcList) {
			
    if(lookAhead <= 1 || srcList.size() < 3) {
      return srcList;
    }
    
    ArrayList<PVector> nP = new ArrayList<PVector>();
			
    int offset;
    int len;
    int count;
    PVector tmp;
			
    len= srcList.size();
			
    if(lookAhead > len-1) {
      lookAhead = len-1;
    }
    tmp = srcList.get(0);
    nP.add(new PVector(tmp.x , tmp.y));		
    count = 1;
    for(int i=0; i<len; i++) {
      if(i+lookAhead > len) {
        lookAhead = len - i -1;
      }
				
      offset = recursiveToleranceBar(srcList, i, lookAhead, tolerance);
      tmp = srcList.get(i+offset);						
      if(offset>0 && tmp != null) {
        if(count > nP.size() - 1)
          nP.add(new PVector(tmp.x , tmp.y));
        else
          nP.set(count,new PVector(tmp.x , tmp.y));
        i += offset-1;// don't loop through the skipped srcList
        count++;
       }
     }
     tmp = srcList.get(len-1);
     if(count -1 > nP.size() - 1)
        nP.add(new PVector(tmp.x , tmp.y));
     else
       nP.set(count - 1, new PVector(tmp.x , tmp.y));
     return nP;
  }
		
		
  // this function is called by simplifyLang
  int recursiveToleranceBar(ArrayList<PVector> srcList, int i, float lookAhead, float tolerance) {
			
    float n = lookAhead;
    PVector cP;
    PVector cLP; 
    PVector v1;
    PVector v2;
    PVector tmp;
    float angle; 
    float dx; 
    float dy;
			
    cP = srcList.get(i);// current point
			
    // check was hier abgeht... hier könnte ne nullpointer exception passierne, oder sowas			
    /*try {
      if(srcList.get((int)(i+n) == null) return 0;
    } catch (IndexOutOfBoundsException) {
      //return 0;
    }*/
    if((int)(i+n) > srcList.size() -1)
      return 0;
    
			
    // the vector through the current point and the max look ahead point
    tmp = srcList.get((int)(i+n));
    v1 = new PVector(tmp.x - cP.x, tmp.y - cP.y);
    // loop through the intermediate srcList

    for(int j=1; j<=n; j++) {
      // the vector	through the current point and the current intermediate point
      cLP = srcList.get(i+j); // current look ahead point
      v2 = new PVector(cLP.x - cP.x, cLP.y - cP.y);
      angle = acos((v1.x * v2.x + v1.y * v2.y) / (sqrt(v1.y * v1.y + v1.x * v1.x) * sqrt(v2.y * v2.y + v2.x * v2.x)) );
      
      // if(isNaN(angle)){angle = 0;} --> Don't know how to translat this to processing...
      
      // the hypothenuse is the line between the current point and the current intermediate point
      dx = cP.x - cLP.x; 
      dy = cP.y - cLP.y;
      float lH = sqrt(dx*dx+dy*dy);// lenght of hypothenuse

				
      // length of opposite leg / perpendicular offset 	
      if( sin(angle) * lH >= tolerance) {// too long, exceeds tolerance
	n--;
	if(n>0) {// back the vector up one point
	  //trace('== recursion, new lookAhead '+n);
	  return recursiveToleranceBar(srcList, i, n, tolerance);
        } else {
	  //trace('== return 0, all exceed tolerance');
	  return 0;// all intermediate srcList exceed tolerance
        }					
      }
    }
    return (int)n;
  }
		
// this function is called by simplifyLang
float recursiveToleranceBar_old(ArrayList<PVector> srcList, int i, float lookAhead, float tolerance) {
			
  float n = lookAhead;
			
  PVector cP; 
  PVector cLP;
  PVector v1;
  PVector v2;
  float angle;
  float dx;
  float dy;
  float res = 0;
  
  cP = srcList.get(i);// current point
  // the vector through the current point and the max look ahead point
  v1 =  new PVector(srcList.get((int)(i+n)).x - cP.x, srcList.get((int)(i+n)).y - cP.y);
  // loop through the intermediate srcList
			
  for(int j=1; j<=n; j++) {
    // the vector	through the current point and the current intermediate point
    cLP =  srcList.get(i+j); // current look ahead point
    v2 = new PVector(cLP.x - cP.x, cLP.y - cP.y);
    angle = acos( (v1.x * v2.x + v1.y * v2.y) / (sqrt(v1.y * v1.y + v1.x * v1.x) * sqrt(v2.y * v2.y + v2.x * v2.x)) );
    
    // if(isNaN(angle)){angle = 0;} --> again how should I translate this to Processing?! Float.isNan(value)
    // the hypothenuse is the line between the current point and the current intermediate point
    dx = cP.x - cLP.x; dy = cP.y - cLP.y;
    float lH = sqrt(dx*dx+dy*dy);// lenght of hypothenuse

    // length of opposite leg / perpendicular offset 	
    if( sin(angle) * lH >= tolerance) {// too long, exceeds tolerance
      n--;
      if(n>0) {// back the vector up one point
        //trace('== recursion, new lookAhead '+n);
	res = recursiveToleranceBar(srcList, i, n, tolerance);
	break;
	//return recursiveToleranceBar(srcList, i, n, tolerance);
      } else {
	res = 0;
        break;
        //trace('== return 0, all exceed tolerance');
	//return 0;// all intermediate srcList exceed tolerance
      }
					
    }
   }
   return res;
  }	
}
